package cn.sc.summer.gateway.config.loadbalance;

import cn.hutool.core.collection.CollUtil;
import cn.sc.summer.gateway.config.CustomizeLoadBalance;
import cn.sc.summer.gateway.model.NacosLoadBalanceRule;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.naming.pojo.Instance;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 类名：自定义负载均衡实现需要实现 ReactorServiceInstanceLoadBalancer 接口 以及重写choose方法
 *
 * @author a-xin
 * @date 2024/5/9 13:54
 */
@Slf4j
public class NacosWeightRule extends NacosLoadBalanceRule {

    private static final long serialVersionUID = -1310322138410473942L;

    public NacosWeightRule(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
                           String serviceId,
                           NacosDiscoveryProperties nacosDiscoveryProperties) {
        super(serviceInstanceListSupplierProvider, serviceId, nacosDiscoveryProperties);
    }

    /**
     * 对所有的服务列表进行过滤
     *
     * @param serverInstances 所有服务信息
     * @return 过滤后的服务列表
     */
    @Override
    protected List<ServiceInstance> filterInstance(List<ServiceInstance> serverInstances) {
        return serverInstances;
    }

    /**
     * 获取适当的服务进行执行
     *
     * @param serverInstances 所有服务信息
     * @return 过滤后的服务
     */
    @Override
    protected ServiceInstance getOneInstance(List<ServiceInstance> serverInstances) {
        if (CollUtil.isEmpty(serverInstances)) {
            throw new RuntimeException("-> Services [" + serviceId + "] that don't exist!");
        }
        // 获取当前服务所在的集群名称
        String currentServiceName = nacosDiscoveryProperties.getClusterName();
        // 过滤在同一集群下注册的服务 根据集群名称筛选的集合
        List<ServiceInstance> sameClusterNameInstList = serverInstances.stream()
                .filter(instance -> StringUtils.equals(instance.getMetadata().get(CLUSTER), currentServiceName))
                .collect(Collectors.toList());
        ServiceInstance sameClusterNameInst;
        if (sameClusterNameInstList.isEmpty()) {
            // 如果为空，则根据权重直接过滤所有服务列表
            sameClusterNameInst = getHostByRandomWeight(serverInstances);
        } else {
            // 如果不为空，则根据权重直接过滤所在集群下的服务列表
            sameClusterNameInst = getHostByRandomWeight(sameClusterNameInstList);
        }
        log.info("-! LoadBalancer to the service: " + serviceId + " -> [" + sameClusterNameInst.getHost() + ":" + sameClusterNameInst.getPort() + "]");
        return sameClusterNameInst;
    }

    private ServiceInstance getHostByRandomWeight(List<ServiceInstance> sameClusterNameInstList) {

        List<Instance> list = new ArrayList<>();
        List<String> ipList = new ArrayList<>();
        Map<String, ServiceInstance> dataMap = new HashMap<>();
        sameClusterNameInstList.forEach(instance -> {
            Instance ins = new Instance();
            Map<String, String> metadata = instance.getMetadata();

            ins.setInstanceId(metadata.get(INSTANCE_ID));
            ins.setWeight(new BigDecimal(metadata.get(WEIGHT)).doubleValue());
            ins.setClusterName(metadata.get(CLUSTER));
            ins.setEphemeral(Boolean.parseBoolean(metadata.get(EPHEMERAL)));
            ins.setHealthy(Boolean.parseBoolean(metadata.get(HEALTHY)));

            ins.setPort(instance.getPort());
            ins.setIp(instance.getHost());
            ins.setServiceName(instance.getServiceId());
            ins.setMetadata(metadata);

            ipList.add(instance.getHost() + ":" + instance.getPort());
            list.add(ins);
            dataMap.put(metadata.get(INSTANCE_ID), instance);
        });
        // 调用nacos官方提供的负载均衡权重算法
        log.debug("-> The {} server list: {}", serviceId, String.join(",", ipList));
        Instance hostByRandomWeightCopy = CustomizeLoadBalance.getHostByRandomWeightCopy(list);
        // 根据最终ID获取需要返回的实例对象
        return dataMap.get(hostByRandomWeightCopy.getInstanceId());
    }

}
